package com.sky.service.common.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.hxc.pay.CommonPayTemplate;
import com.hxc.pay.NativePayTemplate;
import com.sky.constant.BaseServiceConstant;
import com.sky.constant.MessageConstant;
import com.sky.constant.PayStatusConstant;
import com.sky.context.BaseContext;
import com.sky.dto.*;
import com.sky.exception.*;
import com.sky.mapper.common.MerchantMapper;
import com.sky.mapper.common.OrderDetailMapper;
import com.sky.mapper.common.OrdersMapper;
import com.sky.mapper.user.AddressBookMapper;
import com.sky.mapper.user.ShoppingCartMapper;
import com.sky.mapper.user.UserMapper;
import com.sky.pojo.*;
import com.sky.result.PageResult;
import com.sky.service.common.OrderDispatchService;
import com.sky.service.common.OrderService;
import com.sky.service.user.PrePayUrlNotifyService;
import com.sky.utils.MapUtils;
import com.sky.vo.OrderAdviceInfo;
import com.sky.vo.OrderStatisticsVO;
import com.sky.vo.OrderSubmitVO;
import com.sky.vo.OrderVO;
import com.sky.websocket.WebSocketServer;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 订单业务实现
 *
 * @author zgp
 * @version V1.0
 * @date 2025-01-28 11:36
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {

    private final ShoppingCartMapper shoppingCartMapper;
    private final MerchantMapper merchantMapper;
    private final AddressBookMapper addressBookMapper;
    private final OrdersMapper ordersMapper;
    private final OrderDetailMapper orderDetailMapper;
    private final UserMapper userMapper;
    private final NativePayTemplate nativePayTemplate;
    private final PrePayUrlNotifyService prePayUrlNotifyService;
    private final WebSocketServer webSocketServer;
    private final CommonPayTemplate commonPayTemplate;
    private final OrderDispatchService orderDispatchService;


    @Override
    public String getOrderEstimatedDeliveryTime(Long shopId, String customerAddress) {
        // 校验参数

        // 业务处理：预计送达时间 = 当前时间+菜品制作时间+配送时间+预留时间（10分钟）
        // 1. 查询当前用户购物车数据，计算菜品制作时间
        long dishOrSetmealMakeTime = shoppingCartMapper.reportGoodsMakeTimeByUserId(BaseContext.getCurrentId());
        log.info("购物车菜品制作时间：{} s", dishOrSetmealMakeTime * 60);
        // 2. 计算配送时间
        Merchant merchant = merchantMapper.selectById(shopId);
        Map<String, String> durationAndDistanceMap = MapUtils.gaodeTwoPointCostDistanceAndTimeByAddress(merchant.getShopAddress(), customerAddress);
        Long durationTime = Long.valueOf(durationAndDistanceMap.get("duration"));
        log.info("预计配送时间：{} s", durationTime);
        // 3. 计算预计送达时间
        log.info("当前时间：{}", LocalDateTime.now());
        LocalDateTime estimatedDeliveryTime = LocalDateTime.now()
                .plusSeconds(dishOrSetmealMakeTime * 60)
                .plusSeconds(durationTime)
                .plusSeconds(10 * 60);
        log.info("订单预计送达时间：{} s", estimatedDeliveryTime);
        return estimatedDeliveryTime.toString();
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public OrderSubmitVO submitOrders(OrdersSubmitDTO ordersSubmitDTO) {
        // 参数校验
        if (ObjectUtil.isNull(ordersSubmitDTO) ||
                (ObjectUtil.isNull(ordersSubmitDTO.getAddressBookId())
                        || ObjectUtil.isNull(ordersSubmitDTO.getAmount())
                        || ObjectUtil.isNull(ordersSubmitDTO.getDeliveryStatus())
                        || ObjectUtil.isNull(ordersSubmitDTO.getEstimatedDeliveryTime())
                        || ObjectUtil.isNull(ordersSubmitDTO.getPackAmount())
                        || ObjectUtil.isNull(ordersSubmitDTO.getPayMethod())
                        || ObjectUtil.isNull(ordersSubmitDTO.getTablewareNumber())
                        || ObjectUtil.isNull(ordersSubmitDTO.getTablewareStatus())
                )) {
            throw new ParameterInvalidException(MessageConstant.PARAMES_IS_INVALID);
        }
        // 业务处理
        Long userId = BaseContext.getCurrentId();
        LocalDateTime now = LocalDateTime.now();
        List<ShoppingCart> shoppingCarts = shoppingCartMapper.selectByUserId(userId);
        AddressBook addressBook = addressBookMapper.selectById(ordersSubmitDTO.getAddressBookId());
        User user = userMapper.selectById(userId);
        // 1. 保存订单基本信息
        Orders orders = new Orders();
        BeanUtils.copyProperties(ordersSubmitDTO, orders);
        // 使用当前时间纳秒值作为订单号
        orders.setNumber(System.currentTimeMillis() + "" + now.getNano());
        orders.setStatus(Orders.PENDING_PAYMENT);
        orders.setUserId(userId);
        orders.setOrderTime(now);
        orders.setPayStatus(PayStatusConstant.UNPAID);
        orders.setPhone(addressBook.getPhone());
        orders.setAddress(addressBook.getProvinceName() + addressBook.getCityName() + addressBook.getDistrictName() + addressBook.getDetail());
        orders.setConsignee(addressBook.getConsignee());
        orders.setUserName(user.getName());
        // 出餐时间
        Long cartItemMakeTime = shoppingCartMapper.reportGoodsMakeTimeByUserId(userId);
        orders.setMealTime(now.plusSeconds(cartItemMakeTime * 60));
        ordersMapper.insert(orders);
        // 2. 保存订单商品信息
        List<OrderDetail> orderDetails = shoppingCarts.stream()
                .map(cart -> {
                    OrderDetail orderDetail = new OrderDetail();
                    BeanUtils.copyProperties(cart, orderDetail);
                    orderDetail.setOrderId(orders.getId());
                    return orderDetail;
                }).toList();
        orderDetailMapper.insertOfBatch(orderDetails);
        // 3. 清空购物车信息
        shoppingCartMapper.deleteByUserId(userId);
        // 创建微信扫码支付系统订单
        List<String> goodsNames = orderDetails.stream().map(o -> o.getName()).toList();
        String prePayUrl = nativePayTemplate.createOrder(orders.getAmount().intValue() * 100, goodsNames.toString(), orders.getNumber());
        // sse推送支付url
        prePayUrlNotifyService.saveNotify(prePayUrl);
        // 4. 构建返回数据
        OrderSubmitVO orderSubmitVO = OrderSubmitVO.builder()
                .id(orders.getId())
                .orderNumber(orders.getNumber())
                .orderAmount(orders.getAmount())
                .orderTime(orders.getOrderTime())
                .build();
        return orderSubmitVO;
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void updateByOrderNumber(Orders orders) {
        // 校验参数
        if (ObjectUtil.isNull(orders) || ObjectUtil.isNull(orders.getNumber())) {
            throw new OrderBusinessException(MessageConstant.ORDERS_NUMBER_IS_NULL);
        }
        // 处理业务
        ordersMapper.updateByNumber(orders);
    }

    @Override
    public void reminder(Long id) {
        if (ObjectUtil.isNull(id)) {
            throw new ParameterIsNullException(MessageConstant.ID_IS_NULL);
        }
        Orders orders = ordersMapper.selectByIdOrNumber(id, null);
        if (ObjectUtil.isNotNull(orders)) {
            // 通知商户用户催单提醒
            OrderAdviceInfo adviceInfo = OrderAdviceInfo.builder()
                    .orderId(orders.getId().toString())
                    .content(orders.getNumber())
                    .type(2)
                    .build();
            webSocketServer.sendReminder(JSON.toJSONString(adviceInfo));
        }
    }

    @Override
    public PageResult<Orders> getUserHistoryOrders(OrdersPageQueryDTO ordersPageQueryDTO) {
        // 校验参数
        int pageSize = ordersPageQueryDTO.getPageSize();
        int page = ordersPageQueryDTO.getPage();
        // 校验参数
        if (pageSize < BaseServiceConstant.ZERO || page < BaseServiceConstant.ZERO) {
            throw new ParameterInvalidException(MessageConstant.PAGE_PARAMETER_IS_INVALID);
        }
        Long userId = BaseContext.getCurrentId();
        // 构建查询参数
        Orders orders = Orders.builder()
                .status(ordersPageQueryDTO.getStatus())
                .userId(userId)
                .build();
        PageHelper.startPage(page, pageSize);
        Page<Orders> ordersPage = ordersMapper.selectUserHistoryOrders(orders);
        return new PageResult<>(ordersPage.getTotal(), ordersPage.getResult());
    }

    @Override
    public PageResult<Orders> getUserRefundOrders(OrdersPageQueryDTO ordersPageQueryDTO) {
        // 校验参数
        int pageSize = ordersPageQueryDTO.getPageSize();
        int page = ordersPageQueryDTO.getPage();
        // 校验参数
        if (pageSize < BaseServiceConstant.ZERO || page < BaseServiceConstant.ZERO) {
            throw new ParameterInvalidException(MessageConstant.PAGE_PARAMETER_IS_INVALID);
        }
        if (ObjectUtil.isNull(ordersPageQueryDTO.getPayStatus())) {
            ordersPageQueryDTO.setPayStatus(PayStatusConstant.REFUND);
        }
        // 构建查询参数
        Orders orders = Orders.builder()
                .payStatus(ordersPageQueryDTO.getPayStatus())
                .userId(BaseContext.getCurrentId())
                .build();
        PageHelper.startPage(page, pageSize);
        Page<Orders> ordersPage = ordersMapper.selectUserHistoryOrders(orders);
        return new PageResult<>(ordersPage.getTotal(), ordersPage.getResult());
    }

    @Override
    public Orders getOrdersDetail(Long id) {
        if (ObjectUtil.isNull(id)) {
            throw new ParameterInvalidException(MessageConstant.ID_IS_NULL);
        }
        return ordersMapper.selectOrderDetailById(id);
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void repetitionBuyOrders(Long id) {
        if (ObjectUtil.isNull(id)) {
            throw new ParameterInvalidException(MessageConstant.ID_IS_NULL);
        }
        // 查询当前id是否存在订单
        Orders orders = ordersMapper.selectOrderDetailById(id);
        if (ObjectUtil.isNull(orders)) {
            throw new BaseException(MessageConstant.ORDER_NOT_FOUND);
        }
        // 构建订单数据
        LocalDateTime now = LocalDateTime.now();
        orders.setNumber(System.currentTimeMillis() + "" + now.getNano());
        orders.setStatus(Orders.PENDING_PAYMENT);
        orders.setOrderTime(now);
        orders.setCheckoutTime(null);
        orders.setPayStatus(PayStatusConstant.UNPAID);
        orders.setCancelReason(null);
        orders.setCancelTime(null);
        orders.setRejectionReason(null);
        // 出餐时间
        Long cartItemMakeTime = ordersMapper.reportOrdersGoodsMakeTimeById(id);
        orders.setMealTime(now.plusSeconds(cartItemMakeTime * 60));
        // 预计送达时间计算
        Merchant merchant = merchantMapper.selectById(orders.getShopId());
        Map<String, String> durationAndDistanceMap = MapUtils.gaodeTwoPointCostDistanceAndTimeByAddress(merchant.getShopAddress(), orders.getAddress());
        // 配送时间
        Long durationTime = Long.valueOf(durationAndDistanceMap.get("duration"));
        // 预计送达时间=菜品制作时间+配送时间+预留10分钟
        LocalDateTime estimatedDeliveryTime = LocalDateTime.now()
                .plusSeconds(cartItemMakeTime * 60)
                .plusSeconds(durationTime)
                .plusSeconds(10 * 60);
        orders.setEstimatedDeliveryTime(estimatedDeliveryTime);
        // 新增订单基本数据
        ordersMapper.insert(orders);
        // 新增订单详情数据
        List<OrderDetail> orderDetailList = orders.getOrderDetailList();
        if (ObjectUtil.isNotNull(orderDetailList)) {
            orderDetailList.forEach(o -> o.setOrderId(orders.getId()));
            orderDetailMapper.insertOfBatch(orderDetailList);
        }
        // 向微信支付系统创建订单
        List<String> names = orderDetailList.stream().map(o -> o.getName()).toList();
        String prePayUrl = nativePayTemplate.createOrder(orders.getAmount().intValue() * 100, names.toString(), orders.getNumber());
        // sse推送支付消息
        prePayUrlNotifyService.saveNotify(prePayUrl);
    }

    @Override
    public void cancelOrders(OrdersCancelDTO ordersCancelDTO) {
        // 1. 参数校验
        if (ObjectUtil.isNull(ordersCancelDTO) || ObjectUtil.isNull(ordersCancelDTO.getId())) {
            throw new ParameterIsNullException(MessageConstant.ID_IS_NULL);
        }
        if (StrUtil.isEmpty(ordersCancelDTO.getCancelReason())) {
            throw new ParameterIsNullException("订单取消原因不能为空");
        }
        Long id = ordersCancelDTO.getId();
        // 2. 查询原订单
        Orders orignalOrders = ordersMapper.selectByIdOrNumber(id, null);
        if (ObjectUtil.isNull(orignalOrders)) {
            throw new BaseException(MessageConstant.ORDER_NOT_FOUND);
        }
        // 3. 根据业务规则判断当前订单是否允许取消
        Integer status = orignalOrders.getStatus();
        if (!Orders.PENDING_PAYMENT.equals(status)
                && !Orders.TO_BE_CONFIRMED.equals(status)
                && !Orders.CONFIRMED.equals(status)
                && !Orders.TOBEDELIVERED.equals(status)
                && !Orders.COMPLETE_ORDER.equals(status)) {
            // 非待付款1、已付款待接单2，已接单待派送3、派送中5、已完成6，状态订单不能进行取消操作
            throw new BaseException(MessageConstant.ORDER_STATUS_ERROR);
        }
        // 4.  部分取消订单退款处理：待派送2、派送中5、已完成6订单
        String number = orignalOrders.getNumber();
        if(!Orders.PENDING_PAYMENT.equals(status)){
            int ordersAmount = orignalOrders.getAmount().intValue() * 100;
            int refundAmount = ordersAmount;
            // 生成唯一退款订单号
            String outRefundNo = System.currentTimeMillis() + "" + LocalDateTime.now().getNano();
            commonPayTemplate.refund(refundAmount, ordersAmount, number, outRefundNo);
        }
        // 5. 派送中订单取消通知处理
        if(Orders.TOBEDELIVERED.equals(status)){
            // TODO 派送中订单通知骑手取消订单
            // webSocketServer.sendRiderInfo();
        }
        // 6. 订单取消处理
        Orders orders = Orders.builder()
                .number(number)
                .status(Orders.CANCELLED_ORDER)
                .cancelReason(ordersCancelDTO.getCancelReason())
                .cancelTime(LocalDateTime.now())
                .build();
        ordersMapper.updateByNumber(orders);
    }

    @Override
    public PageResult<OrderVO> conditionSearchOrders(OrdersPageQueryDTO dto) {
        // 参数校验
        if (ObjectUtil.isNull(dto) || dto.getPage() < BaseServiceConstant.ZERO || dto.getPageSize() < BaseServiceConstant.ZERO) {
            throw new ParameterInvalidException(MessageConstant.PAGE_PARAMETER_IS_INVALID);
        }
        // 构建查询条件
        Page<Orders> page = PageHelper.startPage(dto.getPage(), dto.getPageSize());
        List<OrderVO> orderVOS = ordersMapper.selectPageByConditions(dto);
        if (ObjectUtil.isNotEmpty(orderVOS)) {
            orderVOS.forEach(o -> {
                List<OrderDetail> orderDetailList = o.getOrderDetailList();
                // 构建
                String orderDishesStr = "";
                if (ObjectUtil.isNotEmpty(orderDetailList)) {
                    orderDishesStr = orderDetailList.stream().map(od -> od.getName()).collect(Collectors.joining("，"));
                }
                o.setOrderDishes(orderDishesStr);
            });
        }
        return new PageResult<>(page.getTotal(), orderVOS);
    }

    @Override
    public OrderStatisticsVO orderStatistics() {
        List<OrdersStatusCountDTO> ordersStatusCountDTOS = ordersMapper.statisticsOrderNumByStatus();
        OrderStatisticsVO orderStatisticsVO = null;
        // 转换统计结果数据格式为Map格式
        if (ObjectUtil.isNotEmpty(ordersStatusCountDTOS)) {
            Map<Integer, Integer> statisticsResult = ordersStatusCountDTOS.stream().collect(Collectors.toMap(k -> k.getStatus(), v -> v.getCount()));
            orderStatisticsVO = OrderStatisticsVO.builder()
                    .waiting4MerchantReceiveOrders(statisticsResult.get(Orders.TO_BE_CONFIRMED))
                    .waitng4RiderReceiveOrders(statisticsResult.get(Orders.CONFIRMED))
                    .toBeDeliveryOnProgressOrders(statisticsResult.get(Orders.TOBEPICKEDUP))
                    .toBeArrivedOrders(statisticsResult.get(Orders.TOBEDELIVERED))
                    .completedOrders(statisticsResult.get(Orders.COMPLETE_ORDER))
                    .canceledOrders(statisticsResult.get(Orders.CANCELLED_ORDER))
                    .build();
        }
        return orderStatisticsVO;
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void rejectionOrder(OrdersRejectionDTO ordersRejectionDTO) {
        if (ObjectUtil.isNull(ordersRejectionDTO)
                || ObjectUtil.isNull(ordersRejectionDTO.getId())
                || ObjectUtil.isNull(ordersRejectionDTO.getRejectionReason())) {
            throw new ParameterIsNullException(MessageConstant.PARAMES_IS_NULL);
        }
        // 查询退款订单
        Orders ordersById = ordersMapper.selectByIdOrNumber(ordersRejectionDTO.getId(), null);
        if (ObjectUtil.isNull(ordersById)) {
            throw new BaseException(MessageConstant.ORDER_NOT_FOUND);
        }
        String number = ordersById.getNumber();
        if (PayStatusConstant.PAID.equals(ordersById.getPayStatus())) {
            // 拒单业务1：已支付订单退款处理
            int ordersAmount = ordersById.getAmount().intValue() * 100;
            int refundAmount = ordersAmount;
            String outRefundNo = System.currentTimeMillis() + "" + LocalDateTime.now().getNano();
            commonPayTemplate.refund(refundAmount, ordersAmount, number, outRefundNo);
        }
        // 拒单业务2：修改订单状态为已取消
        Orders orders = Orders.builder()
                .number(number)
                .rejectionReason(ordersRejectionDTO.getRejectionReason())
                .status(Orders.CANCELLED_ORDER)
                .build();
        ordersMapper.updateByNumber(orders);
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Courier confirmOrders(OrdersConfirmDTO ordersConfirmDTO) {
        // 校验参数
        if(ObjectUtil.isNull(ordersConfirmDTO) || ObjectUtil.isNull(ordersConfirmDTO.getId())){
            throw new ParameterInvalidException(MessageConstant.ID_IS_NULL);
        }
        // 查询原始订单
        Orders orders = ordersMapper.selectByIdOrNumber(ordersConfirmDTO.getId(), null);
        if(ObjectUtil.isNull(orders)){
            throw new BaseException(MessageConstant.ORDER_NOT_FOUND);
        }
        // 查找骑手：根据商家位置获取附近10km范围内在线骑手
        Courier courier = orderDispatchService.dispatch(orders);
        if(ObjectUtil.isNull(courier)){
            // 如第一次未调度到骑手，可继续查找3次
            for (int i = 0; i < 3; i++) {
                courier = orderDispatchService.dispatch(orders);
                if(!ObjectUtil.isNull(courier)){
                    break;
                }
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    log.error(e.getMessage(), e );
                }
            }
        }
        if(ObjectUtil.isNull(courier)){
            throw new NoCourierException("暂未调度到骑手，请稍后再试。");
        }

        // 修改订单状态-->3（商家接单，待骑手接单）
        orders.setStatus(Orders.CONFIRMED);
        ordersMapper.updateByNumber(orders);
        return courier;
    }
}
